#include "Semaforo.h"
#include <BaseException.h>
#include <LockFile.h>

#include <cstring>
#include <sstream>
#include <errno.h>

Semaforo::Semaforo () : m_Id(-1) {
}

Semaforo::Semaforo ( const std::string& nombre,const int valorInicial, const char projectId )
: m_Id(-1) {
	this->crear(nombre, valorInicial, projectId);
}

Semaforo::Semaforo ( const std::string& nombre, const char projectId )
: m_Id(-1) {
	this->abrir(nombre, projectId);
}

Semaforo::~Semaforo() {
}

void Semaforo::abrir( const std::string& nombre, const char projectId ) {
	bool crearSiNoExiste = false;
	this->getSem(nombre, crearSiNoExiste, projectId);
}

void Semaforo::crear( const std::string& nombre, const int valorInicial, const char projectId ) {
	bool crearSiNoExiste = true;
	this->getSem(nombre, crearSiNoExiste, projectId);
	
	if(this->inicializar(valorInicial) < 0)
	{
		this->eliminar();
		
		std::stringstream eMsg;
		eMsg << "El semaforo "+ nombre +" no pudo inicializarse.";
		eMsg << " Error " << errno << ":" << strerror(errno);
		throw BaseException(eMsg.str());
	}
}

bool Semaforo::estaCreado() const
{
	return (m_Id >= 0);
}

void Semaforo::getSem( const std::string& nombre, bool crearSiNoExiste, const char projectId )
{
	if(this->estaCreado())
		throw BaseException("El semaforo "+m_NombreSemaforo+" ya esta inicializado");
		
	key_t clave = ftok ( nombre.c_str(), projectId);
	if ( clave < 0 )
	{
		std::stringstream eMsg;
		eMsg << "Ocurrio un error al obtener la clave para el archivo " << nombre;
		eMsg << ". Error " << errno << " en ftok(): " << strerror(errno);
		throw BaseException(eMsg.str());
	}
	
	// Creo un lock para evitar carreras en la creacion del semaforo
	std::string lockNameAux = nombre + "." + projectId + ".semaux";
	LockFile locker(lockNameAux);
	
	int semflg = (crearSiNoExiste) ? 0666 | IPC_CREAT | IPC_EXCL : 0666 ;
	
	locker.tomarLock();
	m_Id = semget ( clave,1, semflg);
	locker.liberarLock();
	locker.cerrar();
	locker.eliminar();
	
	if(m_Id < 0)
	{
		std::stringstream eMsg;
		eMsg << "El semaforo " << nombre << " no pudo ";
		eMsg << ((crearSiNoExiste) ? "crearse" : "abrirse");
		eMsg << ". Error " << errno << ":" << strerror(errno);
		throw BaseException(eMsg.str(), errno);
	}
	
	m_NombreSemaforo = nombre;
}

int Semaforo::inicializar(const int valorInicial) const {

	union semnum {
		int val;
		struct semid_ds* buf;
		ushort* array;
	};

	semnum init;
	init.val = valorInicial;
	int resultado = semctl ( m_Id, 0, SETVAL, init );
	return resultado;
}

int Semaforo :: p () const {
	if(!this->estaCreado())
		throw BaseException("No se creo o inicializo el semaforo correctamente");

	struct sembuf operacion;

	operacion.sem_num = 0;	// numero de semaforo
	operacion.sem_op  = -1;	// restar 1 al semaforo
	operacion.sem_flg = SEM_UNDO;

	int resultado = semop ( m_Id, &operacion, 1 );
	if(resultado < 0)
	{
		std::stringstream eMsg("Ocurrio un error al liberar al intentar tomar el semaforo ");
		eMsg << "Error " << errno << ":" << strerror(errno);
		throw BaseException(eMsg.str());
	}
	return resultado;
}

int Semaforo :: v () const {
	if(!this->estaCreado())
		throw BaseException("No se creo o inicializo el semaforo correctamente");

	struct sembuf operacion;

	operacion.sem_num = 0;	// numero de semaforo
	operacion.sem_op  = 1;	// sumar 1 al semaforo
	operacion.sem_flg = SEM_UNDO;

	int resultado = semop ( m_Id, &operacion, 1 );
	if(resultado < 0)
	{
		std::stringstream eMsg("Ocurrio un error al liberar al intentar liberar el semaforo ");
		eMsg << "Error " << errno << ":" << strerror(errno);
		throw BaseException(eMsg.str());
	}
	return resultado;
}

void Semaforo :: eliminar () {
	if(m_Id >= 0)
	{
		semctl ( m_Id, 0, IPC_RMID );
		m_Id = -1;
	}
}

void Semaforo :: destruir () {
	m_Id = -1;
}
